﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Linq;
using System.Text;

namespace EventbriteService.Factory
{
    
    internal class ConcreteTypeActivator<T>
    {
        readonly static ConcurrentDictionary<ConstructorInfo, Func<object[], T>> _del = 
            new ConcurrentDictionary<ConstructorInfo, Func<object[], T>>();
       
        public static T CreateInstance(ConstructorInfo constructor, params object[] values)
        {
            Func<object[], T> newDelegate;
           
            if (!_del.TryGetValue(constructor, out newDelegate))
            {
                newDelegate = GetInitializationDelegate(constructor);
                _del.TryAdd(constructor, newDelegate);
            }
            
            return newDelegate(values);
        }

        private static Func<object[], T> GetInitializationDelegate(ConstructorInfo constructor)
        {
            ParameterInfo[] parameterInfo = constructor.GetParameters();
            
            var param = Expression.Parameter(typeof(object[]));
           
            Expression[] arguments = new Expression[parameterInfo.Length];
            for (int i = 0; i < parameterInfo.Length; i++)
            {
                arguments[i] = Expression.Convert
                    (
                        Expression.ArrayIndex(param, Expression.Constant(i)),
                        parameterInfo[i].ParameterType
                     );
            }

            var lambda = Expression.Lambda(typeof(Func<object[], T>),
                                            Expression.New(constructor, arguments),
                                            param);
            return (Func<object[], T>)lambda.Compile();
        }
    }
}
